package com.oh.core.server;

import com.oh.core.server.initializer.ServerInitializer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class SocketServer {

    private final Logger log = LoggerFactory.getLogger(SocketServer.class);
    /**
     * Bootstrap
     * Bootstrap是引导的意思,一个Netty应用通常由一个Bootstrap开始
     * 主要作用是配置整个Netty程序,串联各个组件
     * Netty中Bootstrap类是服务端启动引导类
     */
    private ServerBootstrap serverBootstrap;

    /**
     * netty服务监听端口
     */
    @Value("${netty.port:5710}")
    private int port;
    /**
     * 主线程组数量
     */
    @Value("${netty.bossThread:1}")
    private int bossThread;

    @Value("${netty.workThread:4}")
    private int workThread;

    @Resource
    ServerInitializer serverInitializer;

    /**
     * 启动netty服务器
     */
    public void start() {
        this.init();
        try {
            ChannelFuture channelFuture = this.serverBootstrap.bind(this.port).sync();
            log.info("对外开放端口: {} (TCP)上启动，boss thread {}", this.port, this.bossThread);
            channelFuture.addListener(new GenericFutureListener<Future<? super Void>>() {
                @Override
                public void operationComplete(Future<? super Void> future) throws Exception {
                    if(future.isSuccess()){
                        log.info("服务器正在启动...");
                    }
                    if(future.isDone()){
                        log.info("服务器启动成功...OK");
                    }
                }
            });
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 初始化netty配置
     */
    private void init() {
        /**
         * EventLoop接口
         * NioEventLoop中维护了一个线程和任务队列,支持异步提交任务,线程启动时会调用NioEventLoop的run方法,执行I/O任务和非I/O任务
         * I/O任务即selectionKey中的ready的事件,如accept,connect,read,write等,由processSelectedKeys方法触发
         * 非I/O任务添加到taskQueue中的任务,如register0,bind0等任务,由runAllTasks方法触发
         * 两种任务的执行时间比由变量ioRatio控制,默认为50,则表示允许非IO任务执行的事件与IO任务的执行时间相等
         */
        // 创建两个线程组，bossGroup为接收请求的线程组，一般1-2个就行
        NioEventLoopGroup bossGroup = new NioEventLoopGroup(this.bossThread);
        // 实际工作的线程组
        NioEventLoopGroup workerGroup = new NioEventLoopGroup(this.workThread);
        this.serverBootstrap = new ServerBootstrap();
        this.serverBootstrap.group(bossGroup, workerGroup) // 两个线程组加入进来
                .channel(NioServerSocketChannel.class)  // 配置为nio类型 非阻塞异步服务端TCP Socket 连接 设置boss selector建立channel使用的对象
                .option(ChannelOption.SO_BACKLOG, 128) // boss 等待连接的 队列长度
                .childOption(ChannelOption.SO_KEEPALIVE, true) // 让客户端保持长期活动状态
                .childHandler(serverInitializer); // 加入自己的初始化器
    }
}
